/*
 * =====================================================================================
 *
 *       Filename:  db_manager.h
 *
 *    Description:  db manager class definition.
 *
 *        Version:  1.0
 *        Created:  13/12/2018
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  zhulin, shzhulin3@jd.com
 *        Company:  JD.com, Inc.
 *
 * =====================================================================================
 */

#include <string>
#include <sstream>
#include <sys/time.h>
#include <fstream>
#include "db_manager.h"
#include "utf8_str.h"
using namespace std;

std::map<std::string, int> FieldTypeMap::field_type_map_;

DBManager::DBManager() 
    : mutex_()
{
    aTable = new AppFieldTable();
}

DBManager::~DBManager() {
    if (aTable){
        delete aTable;
    }
    aTable = NULL;
}

uint32_t DBManager::ToInt(const char* str) {
    if (NULL != str)
        return atoi(str);
    else
        return 0;
}

string DBManager::ToString(const char* str) {
    if (NULL != str)
        return str;
    else
        return "";
}

bool DBManager::Init(const SGlobalConfig &global_cfg) {
    if(global_cfg.sAppFieldPath.find("json") != string::npos){
        return InitJsonFile(global_cfg);
    }
    ifstream app_filed_infile;
    app_filed_infile.open(global_cfg.sAppFieldPath.c_str());
    if (app_filed_infile.is_open() == false) {
        log_error("open file error: %s.", global_cfg.sPhoneticPath.c_str());
        return false;
    }
    string str;
    while (getline(app_filed_infile, str))
    {
        vector<string> str_vec = splitEx(str, "\t");
        if (str_vec.size() >= 11) {
            AppFieldInfo info;
            int32_t row_index = 1;
            uint32_t appid = ToInt(str_vec[row_index++].c_str());
            string field_name = ToString(str_vec[row_index++].c_str());
            info.is_primary_key = ToInt(str_vec[row_index++].c_str());
            info.field_type = ToInt(str_vec[row_index++].c_str());
            info.index_tag = ToInt(str_vec[row_index++].c_str());
            row_index++;
            info.segment_tag = ToInt(str_vec[row_index++].c_str());
            info.field_value = ToInt(str_vec[row_index++].c_str());
            row_index++;
            info.segment_feature = ToInt(str_vec[row_index++].c_str());
            if (str_vec.size() >= 12){
                info.index_info = ToString(str_vec[row_index++].c_str());
                log_debug("union index[%s]", info.index_info.c_str());
            }
            aTable->appFieldInfo[appid][field_name] = info;
        }
        else {
            log_error("data format error: %s.", global_cfg.sPhoneticPath.c_str());
            app_filed_infile.close();
            return false;
        }
    }
    app_filed_infile.close();
    return true;
}

bool DBManager::InitJsonFile(const SGlobalConfig &global_cfg){
    std::string sPath = global_cfg.sAppFieldPath;
    std::ifstream iStream(sPath.c_str());
    if (!iStream.is_open()){
        log_error("load %s failed.", sPath.c_str());
        return false;
    }

    FieldTypeMap::Init();
    Json::Reader reader;
    Json::Value oTableDefine;
    if (reader.parse(iStream, oTableDefine)
        && oTableDefine.isMember("tableDefine"))
    {
        Json::Value oTableDefineCont = oTableDefine["tableDefine"];
        if (oTableDefineCont.isArray()){
            for (int i = 0; i < (int)oTableDefineCont.size(); ++i){
                uint32_t uiAppid = 0;
                if (oTableDefineCont[i].isMember("appId")
                    && oTableDefineCont[i]["appId"].isInt()){
                    uiAppid = oTableDefineCont[i]["appId"].asInt();
                }else{
                    log_error("appId is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("fieldDefine") && oTableDefineCont[i]["fieldDefine"].isArray()){
                    Json::Value oFieldDefineCont = oTableDefineCont[i]["fieldDefine"];
                    for (int i = 0; i < (int)oFieldDefineCont.size(); ++i){
                        std::string sFieldName = "";
                        if (oFieldDefineCont[i].isMember("fieldName")
                            && oFieldDefineCont[i]["fieldName"].isString()){
                            sFieldName = oFieldDefineCont[i]["fieldName"].asString();
                        }else{
                            log_error("fieldName is incorrect");
                            return false;
                        }

                        AppFieldInfo oTabInfo;
                        if (oFieldDefineCont[i].isMember("fieldType")
                            && oFieldDefineCont[i]["fieldType"].isInt()){
                            oTabInfo.field_type = oFieldDefineCont[i]["fieldType"].asInt();
                        }else if(oFieldDefineCont[i].isMember("fieldType")
                            && oFieldDefineCont[i]["fieldType"].isString()){
                            oTabInfo.field_type = FieldTypeMap::GetIntType(oFieldDefineCont[i]["fieldType"].asString());
                        }else{
                            log_error("fieldType is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("fieldId")
                            && oFieldDefineCont[i]["fieldId"].isInt()){
                            oTabInfo.field_value = oFieldDefineCont[i]["fieldId"].asInt();
                        }else{
                            log_error("fieldId is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("IsPrimaryKey")
                            && oFieldDefineCont[i]["IsPrimaryKey"].isInt()){
                            oTabInfo.is_primary_key = oFieldDefineCont[i]["IsPrimaryKey"].asInt();
                        }else{
                            log_error("IsPrimaryKey is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("indexTag")
                            && oFieldDefineCont[i]["indexTag"].isInt()){
                            oTabInfo.index_tag = oFieldDefineCont[i]["indexTag"].asInt();
                        }else{
                            log_error("indexTag is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("segmentTag")
                            && oFieldDefineCont[i]["segmentTag"].isInt()){
                            oTabInfo.segment_tag = oFieldDefineCont[i]["segmentTag"].asInt();
                        }else{
                            log_error("segmentTag is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("segmentFeature")
                            && oFieldDefineCont[i]["segmentFeature"].isInt()){
                            oTabInfo.segment_feature = oFieldDefineCont[i]["segmentFeature"].asInt();
                        }else{
                            log_error("segmentFeature is incorrect");
                            return false;
                        }

                        if (oFieldDefineCont[i].isMember("unionField")
                            && oFieldDefineCont[i]["unionField"].isString()){
                            // unionField的格式是：27,1,26，数字代表的是field对应的id值
                            oTabInfo.index_info = oFieldDefineCont[i]["unionField"].asString();
                        }

                        if (oFieldDefineCont[i].isMember("dim")
                            && oFieldDefineCont[i]["dim"].isInt()){
                            oTabInfo.dim = oFieldDefineCont[i]["dim"].asInt();
                        }

                        if (oFieldDefineCont[i].isMember("searchIndexType")
                            && oFieldDefineCont[i]["searchIndexType"].isInt()){
                            oTabInfo.default_index_type = oFieldDefineCont[i]["searchIndexType"].asInt();
                        }

                        if (oFieldDefineCont[i].isMember("metricType")
                            && oFieldDefineCont[i]["metricType"].isString()){
                            oTabInfo.metric_type = oFieldDefineCont[i]["metricType"].asString();
                        }

                        if (oFieldDefineCont[i].isMember("indexType")
                            && oFieldDefineCont[i]["indexType"].isArray()){
                            for(int idx = 0; idx < (int)oFieldDefineCont[i]["indexType"].size(); idx++){
                                if(oFieldDefineCont[i]["indexType"][idx].isString()){
                                    oTabInfo.index_type.push_back(oFieldDefineCont[i]["indexType"][idx].asString());
                                }
                            }
                        }

                        if (oFieldDefineCont[i].isMember("indexDir")
                            && oFieldDefineCont[i]["indexDir"].isArray()){
                            for(int idx = 0; idx < (int)oFieldDefineCont[i]["indexDir"].size(); idx++){
                                if(oFieldDefineCont[i]["indexDir"][idx].isString()){
                                    oTabInfo.index_dir.push_back(oFieldDefineCont[i]["indexDir"][idx].asString());
                                }
                            }
                        }
                        aTable->appFieldInfo[uiAppid][sFieldName] = oTabInfo;
                        log_debug("appid:%d , fieldName:%s , fieldType:%d, IsPrimaryKey:%d , indexTag:%d , segmentTag:%d, segmentFeature:%d , unionField:%s"
                            , uiAppid , sFieldName.c_str() , oTabInfo.field_type
                            , oTabInfo.is_primary_key , oTabInfo.index_tag
                            , oTabInfo.segment_tag , oTabInfo.segment_feature , oTabInfo.index_info.c_str());
                    }
                }else{
                    log_error("fieldDefine is incorrect");
                    return false;
                }
            }
            log_debug("tableDefine size: %d", (int)aTable->appFieldInfo.size());
        }else{
            log_error("tableDefine is incorrect");
            return false;
        }
    }else{
        log_error("config file: %s has error format" , sPath.c_str());
        iStream.close();
        return false;
    }
    return true;
}

bool DBManager::ReplaceAppFieldInfo(string appFieldFile)
{
    {
        CScopedLock autolock(mutex_);
        aTable->appFieldInfo.clear();
    }

    std::ifstream app_filed_infile;
    app_filed_infile.open(appFieldFile.c_str());
    if (!app_filed_infile.is_open()){
        log_error("load %s failed.", appFieldFile.c_str());
        return false;
    }

    Json::Reader reader;
    Json::Value oTableDefine;
    if (reader.parse(app_filed_infile, oTableDefine)
        && oTableDefine.isMember("tableDefine"))
    {
        Json::Value oTableDefineCont = oTableDefine["tableDefine"];
        if (oTableDefineCont.isArray()){
            for (int i = 0; i < (int)oTableDefineCont.size(); ++i){
                uint32_t uiAppid = 0;
                if (oTableDefineCont[i].isMember("appId")
                    && oTableDefineCont[i]["appId"].isInt()){
                    uiAppid = oTableDefineCont[i]["appId"].asInt();
                }else{
                    log_error("appId is incorrect");
                    return false;
                }
                
                std::string sFieldName = "";
                if (oTableDefineCont[i].isMember("fieldName")
                    && oTableDefineCont[i]["fieldName"].isString()){
                    sFieldName = oTableDefineCont[i]["fieldName"].asString();
                }else{
                    log_error("fieldName is incorrect");
                    return false;
                }

                AppFieldInfo oTabInfo;
                if (oTableDefineCont[i].isMember("fieldType")
                    && oTableDefineCont[i]["fieldType"].isInt()){
                    oTabInfo.field_type = oTableDefineCont[i]["fieldType"].asInt();
                }else{
                    log_error("fieldType is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("IsPrimaryKey")
                    && oTableDefineCont[i]["IsPrimaryKey"].isInt()){
                    oTabInfo.is_primary_key = oTableDefineCont[i]["IsPrimaryKey"].asInt();
                }else{
                    log_error("IsPrimaryKey is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("indexTag")
                    && oTableDefineCont[i]["indexTag"].isInt()){
                    oTabInfo.index_tag = oTableDefineCont[i]["indexTag"].asInt();
                }else{
                    log_error("indexTag is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("segmentTag")
                    && oTableDefineCont[i]["segmentTag"].isInt()){
                    oTabInfo.segment_tag = oTableDefineCont[i]["segmentTag"].asInt();
                }else{
                    log_error("segmentTag is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("fieldId")
                    && oTableDefineCont[i]["fieldId"].isInt()){
                    oTabInfo.field_value = oTableDefineCont[i]["fieldId"].asInt();
                }else{
                    log_error("fieldId is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("segmentFeature")
                    && oTableDefineCont[i]["segmentFeature"].isInt()){
                    oTabInfo.segment_feature = oTableDefineCont[i]["segmentFeature"].asInt();
                }else{
                    log_error("segmentFeature is incorrect");
                    return false;
                }

                if (oTableDefineCont[i].isMember("unionField")
                    && oTableDefineCont[i]["unionField"].isString()){
                    // unionField的格式是：27,1,26，数字代表的是field对应的id值
                    oTabInfo.index_info = oTableDefineCont[i]["unionField"].asString();
                }

                log_debug("appid:%d , fieldName:%s , fieldType:%d , fieldId:%d, IsPrimaryKey:%d , indexTag:%d, segmentTag:%d, segmentFeature:%d , unionField:%s"
                    , uiAppid , sFieldName.c_str() , oTabInfo.field_type , oTabInfo.field_value 
                    , oTabInfo.is_primary_key , oTabInfo.index_tag ,  oTabInfo.segment_tag , oTabInfo.segment_feature , oTabInfo.index_info.c_str());

                {
                    CScopedLock autolock(mutex_);
                    aTable->appFieldInfo[uiAppid][sFieldName] = oTabInfo;
                }
            }
            log_debug("appid size: %d", (int)aTable->appFieldInfo.size());
        }else{
            log_error("tableDefine is incorrect");
            return false;
        }
    }else{
        log_error("config file: %s has error format" ,  appFieldFile.c_str());
        app_filed_infile.close();
        return false;
    }
    app_filed_infile.close();
    return true;
}

uint32_t DBManager::GetWordField(uint32_t& segment_tag, uint32_t appid, string field_name, FieldInfo &fieldInfo)
{
    CScopedLock autolock(mutex_);
    uint32_t field_value = 0;
    if (aTable->appFieldInfo.find(appid) != aTable->appFieldInfo.end()) {
        map<string, AppFieldInfo>& field_map = aTable->appFieldInfo[appid];
        if (field_map.find(field_name) != field_map.end()) {
            AppFieldInfo& info = field_map[field_name];
            segment_tag = info.segment_tag;
            fieldInfo.field = info.field_value;
            fieldInfo.field_type = info.field_type;
            fieldInfo.segment_tag = info.segment_tag;
            fieldInfo.segment_feature = info.segment_feature;
            fieldInfo.index_tag = info.index_tag;
            fieldInfo.dim = info.dim;
            fieldInfo.index_type_size = info.index_type.size();
            return info.field_value;
        }
    }
    return field_value;
}

bool DBManager::IsFieldSupportRange(uint32_t appid, uint32_t field_value) {
    CScopedLock autolock(mutex_);
    std::map<uint32_t, std::map<std::string, AppFieldInfo> >::iterator iter = aTable->appFieldInfo.find(appid);
    if (iter != aTable->appFieldInfo.end()) {
        std::map<std::string, AppFieldInfo> map_field = iter->second;
        std::map<std::string, AppFieldInfo>::iterator field_iter = map_field.begin();
        for (; field_iter != map_field.end(); field_iter++) {
            AppFieldInfo field_info = field_iter->second;
            if (field_info.field_value == field_value) {
                if (field_info.segment_tag == 5) {
                    return true;
                }
                break;
            }
        }
    }
    return false;
}

bool DBManager::GetFieldValue(uint32_t appid, string field, uint32_t &field_value){
    CScopedLock autolock(mutex_);
    std::map<uint32_t, std::map<std::string, AppFieldInfo> >::iterator iter = aTable->appFieldInfo.find(appid);
    if (iter != aTable->appFieldInfo.end()) {
        std::map<std::string, AppFieldInfo> map_field = iter->second;
        if(map_field.find(field) != map_field.end()){
            AppFieldInfo appFieldInfo = map_field[field];
            field_value = appFieldInfo.field_value;
            return true;
        }
    }
    return false;
}

bool DBManager::GetFieldType(uint32_t appid, string field, uint32_t &field_type){
    CScopedLock autolock(mutex_);
    std::map<uint32_t, std::map<std::string, AppFieldInfo> >::iterator iter = aTable->appFieldInfo.find(appid);
    if (iter != aTable->appFieldInfo.end()) {
        std::map<std::string, AppFieldInfo> map_field = iter->second;
        if(map_field.find(field) != map_field.end()){
            AppFieldInfo appFieldInfo = map_field[field];
            field_type = appFieldInfo.field_type;
            return true;
        }
    }
    return false;
}

bool DBManager::GetUnionKeyField(uint32_t appid, vector<string> & field_vec){
    CScopedLock autolock(mutex_);
    if (aTable->appFieldInfo.find(appid) != aTable->appFieldInfo.end()) {
        map<string, AppFieldInfo> stMap = aTable->appFieldInfo[appid];
        map<string, AppFieldInfo>::iterator iter = stMap.begin();
        for (; iter != stMap.end(); iter++) {
            AppFieldInfo tInfo = iter->second;
            if(tInfo.field_type == FIELD_INDEX){
                field_vec.push_back(tInfo.index_info);
            }
        }
        return true;
    }
    return false;
}
